package netbolt

import (
	"errors"
	"flag"
	"github.com/samuel/go-zookeeper/zk"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"log"
	"net"
	"os"
	"os/signal"
	"sync"
	"time"
)

const (
	SLOT_COUNT uint16 = 4096 //16384
)

var (
	NetBoltConfig     *Config
	SYSDB_CRERTE_FAIL = errors.New("sys db create fail!")
	Databases         sync.Map //Brown Hu：可以使用普通map
	dbInfos           []NetBoltDB

	minSLOT uint16 = 0
	maxSLOT uint16 = SLOT_COUNT

	indexBucketName = []byte("__sys_idx")
	Oschan          = make(chan os.Signal, 1)

	LIS net.Listener
)

type Config struct {
	Netbolt struct {
		Listen                 string
		DBPath                 string
		UpdateMetaDataInterval int64
		Cluster                bool
		Nodes                  []string
		Zk                     struct {
			Servers []string
			St      int64
		}
	}
}

func UpdateDatabases() {
	dbLock.Lock()
	defer dbLock.Unlock()
	Databases.Range(func(key interface{}, value interface{}) bool {

		(value.(*NetBoltDBInstance)).Update()
		return true
	})
}

func CloseAllDatabases() {
	dbLock.Lock()
	defer dbLock.Unlock()
	Databases.Range(func(key interface{}, value interface{}) bool {

		(value.(*NetBoltDBInstance)).Close()
		return true
	})
}

func GetDatabase(name string) (*NetBoltDBInstance, bool) {
	dbLock.RLock()
	defer dbLock.RUnlock()
	r, ok := Databases.Load(name)
	if ok {
		return r.(*NetBoltDBInstance), ok
	} else {
		return nil, ok
	}
}

func (c *Config) getZkSessionTimeout() time.Duration {
	return time.Duration(c.Netbolt.Zk.St)
}

func newDefaultConfig() *Config {

	conf := new(Config)
	conf.Netbolt.Listen = ":9995"
	conf.Netbolt.DBPath = "./dbstore"
	conf.Netbolt.Zk.Servers = []string{"localhost:2181"}
	conf.Netbolt.Zk.St = 5000
	conf.Netbolt.Cluster = false
	conf.Netbolt.UpdateMetaDataInterval = 50000
	return conf
}

func InitDB() {

	configFileName := flag.String("conf", "", "Input config filename!")
	flag.Parse()
	if flag.Lookup("test.v") == nil {

		signal.Notify(Oschan, os.Interrupt, os.Kill)

		log.SetOutput(os.Stdout)

		NetBoltConfig = newDefaultConfig()

		go func() {
			for {
				time.Sleep(time.Duration(NetBoltConfig.Netbolt.UpdateMetaDataInterval) * time.Millisecond)
				timingUpdateDB()

			}
		}()

		if *configFileName == "" {
			*configFileName = "netbolt.yaml"
		}

		buf, err := ioutil.ReadFile(*configFileName)

		Logger.Printf("Load Config File %s \n", *configFileName)

		if err != nil {
			ErrLog.Panic(err)

		}
		err = yaml.Unmarshal(buf, NetBoltConfig)

		if err != nil {
			ErrLog.Panic(err)
		}

		LIS, err = net.Listen("tcp", NetBoltConfig.Netbolt.Listen)

		if err != nil {
			ErrLog.Panic(err)
		}
		ZkConn, _, err = zk.Connect(NetBoltConfig.Netbolt.Zk.Servers, time.Millisecond*NetBoltConfig.getZkSessionTimeout())

		if err != nil {
			ErrLog.Panic(err)
		}

		//go func() {
		//	for {
		//		select {
		//		case event := <-ZkMonitor:
		//			if OnZkEvent != nil {
		//				OnZkEvent(&event)
		//			}
		//
		//			Logger.Printf("zk event:%v\n", event)
		//		}
		//	}
		//
		//}()

		if _, err := ZKCreateIfNotExist(ZK_ROOT_PATH, []byte(""), 0, zk.WorldACL(zk.PermAll)); err != nil && err != zk.ErrNodeExists {
			ErrLog.Panic(err)
		}

		if _, err := ZKCreateIfNotExist(ZK_DB_PATH, []byte(""), 0, zk.WorldACL(zk.PermAll)); err != nil && err != zk.ErrNodeExists {
			ErrLog.Panic(err)
		}

		//if _, err := ZKCreateIfNotExist(ZK_DB_SYSDB, []byte(""), 0, zk.WorldACL(zk.PermAll)); err != nil && err != zk.ErrNodeExists {
		//	log.Panic(err)
		//}

		exist, err := ExistDB("sys")

		if err != nil {
			ErrLog.Panic(err)
		}

		if !exist {
			dbName := UUID() + ".db"
			sysDB = &NetBoltDB{Name: "sys", FilePath: "sys/" + dbName, Created: time.Now(), Compress: "gzip"}
			created, err := sysDB.create()
			if err != nil {
				ErrLog.Panic(err)
			}
			if created {
				Logger.Printf("sys db load success.\n")
			} else {
				ErrLog.Panic(SYSDB_CRERTE_FAIL)
			}
		}
		//else {
		//	sysDB, err = LoadDB("sys")
		//	if err != nil {
		//		log.Panic(err)
		//	} else {
		//		log.Printf("sys db load success:%v\n", sysDB)
		//	}
		//}

		//_, err = sysDB.open()
		//
		//if err != nil {
		//	log.Panic(err)
		//}

		dbInfos, err = ListDB()
		if err != nil {
			ErrLog.Panic(err)
		}

		for _, dbinfo := range dbInfos {
			_, err := dbinfo.open()

			if err != nil {
				ErrLog.Panic(err)
			}

			//Databases.Store(dbinfo.Name, dbInstance)
		}
		myId = readMyId()
		//Logger.Println("Load success!")
		//
		//Databases.Range(func(key interface{}, value interface{}) bool {
		//	fmt.Printf("%s,%p,%p\n",key, value.(*NetBoltDBInstance).DBInstance,value.(*NetBoltDBInstance).NetBoltDB)
		//	return true
		//})
	}

}
